package com.milhouz.wormdroid.gl.renderer;

import java.util.ArrayList;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import com.milhouz.wormdroid.gl.model.GLSprite;
import com.milhouz.wormdroid.gl.model.hud.IHudSprite;
import com.milhouz.wormdroid.gl.util.Grid;

/**
 * An OpenGL ES renderer based on the GLSurfaceView rendering framework. This class is responsible for drawing a list of renderables to the screen
 * every frame. It also manages loading of textures and (when VBOs are used) the allocation of vertex buffer objects.
 */
public class GameGLRenderer implements Renderer {
	final static float ZOOM_FACTOR_0 = 1f;
	final static float ZOOM_FACTOR_1 = 0.75f;
	final static float ZOOM_FACTOR_2 = 0.5f;

	// Specifies the format our textures should be converted to upon load.
	private static BitmapFactory.Options sBitmapOptions = new BitmapFactory.Options();
	// An array of things to draw every frame.
	ArrayList<GLSprite> mSprites;
	ArrayList<IHudSprite> mHudSprites;
	// A reference to the application context.
	Context mContext;

	public int zx;
	public int zy;
	int screenWidth;
	int screenHeight;
	public boolean isZoomed = false;
	public boolean showHud = true;
	float zoomFactor = ZOOM_FACTOR_0;

	public GameGLRenderer(Context context) {
		// Set our bitmaps to 16-bit, 565 format.
		sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
		mContext = context;
	}

	public int[] getConfigSpec() {
		// We don't need a depth buffer, and don't care about our color depth.
		int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 0, EGL10.EGL_NONE };
		return configSpec;
	}

	public void setSprites(ArrayList<GLSprite> sprites) {
		mSprites = sprites;
	}

	public void setHudSprites(ArrayList<IHudSprite> sprites) {
		mHudSprites = sprites;
	}

	/** Draw the frame */
	public void drawFrame(GL10 gl, int width, int height) {
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
		drawPhysicSprites(gl, width, height);
		if (showHud) {
			drawTextSprites(gl, width, height);
		}
	}

	/** Draws the sprites. */
	void drawPhysicSprites(GL10 gl, int width, int height) {
		Grid.beginDrawing(gl, true);
		if (mSprites != null) {
			for (int x = 0; x < mSprites.size(); x++) {
				mSprites.get(x).draw(gl, zx, zy, isZoomed, zoomFactor);
			}
		}
		Grid.endDrawing(gl);
	}

	/** Draws the texts. */
	void drawTextSprites(GL10 gl, int width, int height) {
		if (mHudSprites != null)
			for (IHudSprite hudSprite : mHudSprites) {
				hudSprite.draw(gl, width, height);
			}
	}

	/* Called when the size of the window changes. */
	public void sizeChanged(GL10 gl, int width, int height) {
		gl.glViewport(0, 0, width, height);
		screenWidth = width;
		screenHeight = height;
		/*
		 * Set our projection matrix. This doesn't have to be done each time we draw, but usually a new projection needs to be set when the viewport
		 * is resized.
		 */
		gl.glMatrixMode(GL10.GL_PROJECTION);
		gl.glLoadIdentity();
		gl.glOrthof(0.0f, width, 0.0f, height, 0f, 1.5f);
		gl.glShadeModel(GL10.GL_FLAT);
		gl.glEnable(GL10.GL_BLEND);
		gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
		gl.glEnable(GL10.GL_TEXTURE_2D);
	}

	/**
	 * Called whenever the surface is created. This happens at startup, and may be called again at runtime if the device context is lost (the screen
	 * goes to sleep, etc). This function must fill the contents of vram with texture data and (when using VBOs) hardware vertex arrays.
	 */
	public void surfaceCreated(GL10 gl) {
		/*
		 * Some one-time OpenGL initialization can be made here probably based on features of this particular context
		 */
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

		gl.glClearColor(0.5f, 0.5f, 0.5f, 1);
		gl.glShadeModel(GL10.GL_SMOOTH); // GL_SMOOTH ????
		gl.glDisable(GL10.GL_DEPTH_TEST);
		gl.glEnable(GL10.GL_TEXTURE_2D);
		/*
		 * By default, OpenGL enables features that improve quality but reduce performance. One might want to tweak that especially on software
		 * renderer.
		 */
		gl.glDisable(GL10.GL_DITHER);
		gl.glDisable(GL10.GL_LIGHTING);

		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		// Initialize Sprites Textures
		if (mSprites != null) {
			for (GLSprite sprite : mSprites) {
				sprite.setTextureName(sprite.loadTextureBitmap(mContext, gl));
			}
		}

		// Initialize Textual sprites
		if (mHudSprites != null) {
			for (IHudSprite hudSprite : mHudSprites) {
				hudSprite.initialize(gl);
			}
		}
	}

	/**
	 * Called when the rendering thread shuts down. This is a good place to release OpenGL ES resources.
	 * 
	 * @param gl
	 */
	public void shutdown(GL10 gl) {
		if (mSprites != null) {

			int lastFreedResource = -1;
			int[] textureToDelete = new int[1];

			for (int x = 0; x < mSprites.size(); x++) {
				int resource = mSprites.get(x).getResourceId();
				if (resource != lastFreedResource) {
					textureToDelete[0] = mSprites.get(x).getTextureName();
					gl.glDeleteTextures(1, textureToDelete, 0);
					mSprites.get(x).setTextureName(0);
				}
			}
		}

		// Shutdown Hud Sprites
		if (mHudSprites != null)
			for (IHudSprite hudSprite : mHudSprites) {
				hudSprite.shutdown(gl);
			}
	}

	public void moveCamera(int dx, int dy) {
		zx = dx;
		zy = dy;
	}

	public void moveCameraWithSprite(GLSprite sprite) {
		if (sprite != null && sprite.isViewCentered) {
			centerCameraOnSprite(sprite);
		}
	}

	public void centerCameraOnSprite(GLSprite sprite) {
		if (sprite != null && isZoomed) {
			zx = (int) (((screenWidth / 2) - sprite.getX()) * zoomFactor);
			zy = -(int) (((screenHeight / 2) - sprite.getY()) * zoomFactor);
		} else if (sprite != null) {
			zx = (int) ((screenWidth / 2) - sprite.getX());
			zy = -(int) ((screenHeight / 2) - sprite.getY());
		}
	}

	public void setScreenDimension(int width, int height) {
		this.screenHeight = height;
		this.screenWidth = width;
	}

	public ArrayList<GLSprite> getSprites() {
		return mSprites;
	}

	public GLSprite getSpriteById(int idSprite) {
		for (GLSprite sprite : mSprites) {
			if (sprite.getId() == idSprite) {
				return sprite;
			}
		}
		return null;
	}

	public void setZoom() {
		if (!isZoomed) {
			zoomFactor = ZOOM_FACTOR_1;
			isZoomed = !isZoomed;
		} else if (isZoomed && zoomFactor == ZOOM_FACTOR_1) {
			zoomFactor = ZOOM_FACTOR_2;
		} else if (isZoomed && zoomFactor == ZOOM_FACTOR_2) {
			zoomFactor = ZOOM_FACTOR_0;
			isZoomed = !isZoomed;
		}
	}

	public boolean isZoomed() {
		return isZoomed;
	}

}
